package net.goutalk.glcs.module.system.service.impl;

import cn.dev33.satoken.session.SaSession;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.bean.BeanUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import net.goutalk.glcs.common.constant.GlobalConstant;
import net.goutalk.glcs.common.enums.AuthorizeType;
import net.goutalk.glcs.common.enums.EnabledMark;
import net.goutalk.glcs.common.enums.MenuType;
import net.goutalk.glcs.common.enums.YesOrNoEnum;
import net.goutalk.glcs.common.model.tree.ITreeNode;
import net.goutalk.glcs.common.utils.TreeUtil;
import net.goutalk.glcs.common.utils.VoToColumnUtil;
import net.goutalk.glcs.module.desktop.entity.DesktopSchema;
import net.goutalk.glcs.module.desktop.entity.UserDesktopRelation;
import net.goutalk.glcs.module.desktop.mapper.DesktopSchemaMapper;
import net.goutalk.glcs.module.desktop.mapper.UserDesktopRelationMapper;
import net.goutalk.glcs.module.desktop.vo.DesktopSchemaInfoVo;
import net.goutalk.glcs.module.magicapi.entity.InterfaceAuth;
import net.goutalk.glcs.module.magicapi.mapper.InterfaceAuthMapper;
import net.goutalk.glcs.module.organization.dto.SetRoleAuthDto;
import net.goutalk.glcs.module.organization.entity.*;
import net.goutalk.glcs.module.organization.mapper.*;
import net.goutalk.glcs.module.organization.entity.Department;
import net.goutalk.glcs.module.organization.entity.Post;
import net.goutalk.glcs.module.organization.entity.Role;
import net.goutalk.glcs.module.organization.entity.UserRoleRelation;
import net.goutalk.glcs.module.organization.mapper.RoleMapper;
import net.goutalk.glcs.module.organization.vo.RoleAuthVo;
import net.goutalk.glcs.module.organization.vo.UserRoleVo;
import net.goutalk.glcs.module.system.entity.*;
import net.goutalk.glcs.module.system.mapper.*;
import net.goutalk.glcs.module.system.service.IAuthorizeService;
import net.goutalk.glcs.module.system.vo.MenuAuthVo;
import net.goutalk.glcs.module.system.vo.MenuFormTreeVo;
import net.goutalk.glcs.module.system.vo.MenuTreeVo;
import net.goutalk.glcs.module.system.vo.PermissionVo;
import lombok.AllArgsConstructor;
import net.goutalk.glcs.module.organization.mapper.UserDeptRelationMapper;
import net.goutalk.glcs.module.organization.mapper.UserPostRelationMapper;
import net.goutalk.glcs.module.organization.mapper.UserRoleRelationMapper;
import net.goutalk.glcs.module.system.entity.*;
import net.goutalk.glcs.module.system.mapper.*;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author tanyujie
 * @since 2022-03-16
 */
@Service
@AllArgsConstructor
public class AuthorizeServiceImpl extends ServiceImpl<AuthorizeMapper, Authorize> implements IAuthorizeService {

    private final UserRoleRelationMapper userRoleRelationMapper;

    private final UserDeptRelationMapper userDeptRelationMapper;

    private final UserPostRelationMapper userPostRelationMapper;

    private final RoleMapper roleMapper;

    private final AuthorizeMapper authorizeMapper;

    private final InterfaceAuthMapper interfaceAuthMapper;

    private final MenuButtonMapper menuButtonMapper;

    private final MenuColumnMapper menuColumnMapper;

    private final MenuFormMapper menuFormMapper;

    private final MenuMapper menuMapper;

    private final DesktopSchemaMapper desktopSchemaMapper;

    private final UserDesktopRelationMapper userDesktopRelationMapper;


    @Override
    public PermissionVo getPermissions() {

        PermissionVo result = new PermissionVo();
        List<MenuAuthVo> authVoList = new ArrayList<>();
        result.setMenuAuthList(authVoList);

        long userId = StpUtil.getLoginIdAsLong();
        List<UserRoleRelation> relations = userRoleRelationMapper.selectList(Wrappers.lambdaQuery(UserRoleRelation.class)
                .eq(UserRoleRelation::getUserId, userId));

        List<Long> relationIds = relations.stream().map(UserRoleRelation::getRoleId).collect(Collectors.toList());

        List<Role> roleList = roleMapper.selectList(Wrappers.lambdaQuery(Role.class)
                .select(Role.class, x -> VoToColumnUtil.fieldsToColumns(UserRoleVo.class).contains(x.getColumn()))
                .in(relationIds.size() > 0, Role::getId, relationIds));

        List<String> roleCodeList = roleList.stream().map(Role::getCode).collect(Collectors.toList());
        List<Long> roleIdList = roleList.stream().map(Role::getId).collect(Collectors.toList());

        SaSession tokenSession = StpUtil.getTokenSession();
        Post post = tokenSession.get(GlobalConstant.LOGIN_USER_POST_INFO_KEY, new Post());
        Department department = tokenSession.get(GlobalConstant.LOGIN_USER_DEPT_INFO_KEY, new Department());

        UserDesktopRelation userDesktopRelation = userDesktopRelationMapper.selectOne(
                Wrappers.lambdaQuery(UserDesktopRelation.class).eq(UserDesktopRelation::getUserId, userId));
        Long desktopId = userDesktopRelation == null ? null : userDesktopRelation.getDesktopId();
        DesktopSchema desktopSchema = desktopSchemaMapper.selectOne(
                Wrappers.lambdaQuery(DesktopSchema.class)
                        .eq(desktopId == null, DesktopSchema::getIsFirst, YesOrNoEnum.YES.getCode())
                        .eq(desktopId != null, DesktopSchema::getId, desktopId));
        result.setDesktopSchema(BeanUtil.toBean(desktopSchema, DesktopSchemaInfoVo.class));

        result.setPostId(post.getId());
        result.setPostName(post.getName());
        result.setDepartmentId(department.getId());
        result.setDepartmentName(department.getName());

        //如果包含超级管理员权限  默认返回所有权限
        List<MenuButton> buttonList = new ArrayList<>();
        List<MenuColumn> columnList = new ArrayList<>();
        List<MenuForm> formList = new ArrayList<>();
        if (roleIdList.contains(GlobalConstant.SUPER_ADMIN_ROLE_ID)) {
            buttonList = menuButtonMapper.selectList(null);
            columnList = menuColumnMapper.selectList(null);
            formList = menuFormMapper.selectList(null);
        } else {
            List<Authorize> authorizeList = authorizeMapper.selectList(Wrappers.lambdaQuery(Authorize.class)
                    .in(roleIdList.size() > 0,Authorize::getRoleId, roleIdList)
            );

            List<Long> buttonIds = authorizeList.stream().filter(x -> x.getAuthorizeType() == AuthorizeType.BUTTON.getCode()).map(Authorize::getObjectId).collect(Collectors.toList());
            if (buttonIds.size() > 0) {
                LambdaQueryWrapper<MenuButton> queryWrapper = Wrappers.lambdaQuery(MenuButton.class);
                queryWrapper.in(MenuButton::getId, buttonIds).select(MenuButton::getMenuId, MenuButton::getCode);
                buttonList = menuButtonMapper.selectList(queryWrapper);
            }

            List<Long> columnIds = authorizeList.stream().filter(x -> x.getAuthorizeType() == AuthorizeType.COLUMN.getCode()).map(Authorize::getObjectId).collect(Collectors.toList());
            if (columnIds.size() > 0) {
                LambdaQueryWrapper<MenuColumn> queryWrapper = Wrappers.lambdaQuery(MenuColumn.class);
                queryWrapper.in(MenuColumn::getId, columnIds).select(MenuColumn::getMenuId,MenuColumn::getCode);
                columnList = menuColumnMapper.selectList(queryWrapper);
            }

            List<Long> formIds = authorizeList.stream().filter(x -> x.getAuthorizeType() == AuthorizeType.FORM.getCode()).map(Authorize::getObjectId).collect(Collectors.toList());
            if (formIds.size() > 0) {
                LambdaQueryWrapper<MenuForm> queryWrapper = Wrappers.lambdaQuery(MenuForm.class);
                queryWrapper.in(MenuForm::getId, formIds);
                formList = menuFormMapper.selectList(queryWrapper);
            }
        }

        List<Menu> menus = menuMapper.selectList(Wrappers.lambdaQuery(Menu.class).eq(Menu::getMenuType, MenuType.FUNCTION.getCode()).select(Menu::getId));

        List<MenuFormTreeVo> formTreeVoList = TreeUtil.build(BeanUtil.copyToList(formList, MenuFormTreeVo.class));
        for (Menu menu : menus) {
            MenuAuthVo vo = new MenuAuthVo();
            vo.setMenuId(menu.getId());
            vo.setButtonAuthCode(buttonList.stream().filter(x -> x.getMenuId().equals(menu.getId())).map(MenuButton::getCode).collect(Collectors.toSet()));
            vo.setColumnAuthCode(columnList.stream().filter(x -> x.getMenuId().equals(menu.getId())).map(MenuColumn::getCode).collect(Collectors.toSet()));
            // 处理表单字段，可能存在子表的情况
            Set<Object> formAuthCodeList= new LinkedHashSet<>(formTreeVoList.size());
            for (MenuFormTreeVo treeVo : formTreeVoList) {
                if (treeVo.getMenuId().equals(menu.getId())) {
                    List<MenuFormTreeVo> children = treeVo.getChildren();
                    if (CollectionUtils.isNotEmpty(children)) {
                        Set<String> subCodeList = new LinkedHashSet<>(children.size());
                        for (MenuFormTreeVo child : children) {
                            subCodeList.add(child.getCode());
                        }
                        Map<String, Set<String>> subTableCodeMap = new HashMap<>(1);
                        subTableCodeMap.put(treeVo.getCode(), subCodeList);
                        formAuthCodeList.add(subTableCodeMap);
                    } else {
                        formAuthCodeList.add(treeVo.getCode());
                    }
                }
            }
            vo.setFormAuthCode(formAuthCodeList);
            authVoList.add(vo);
        }

        // 自定义接口权限
        List<InterfaceAuth> interfaceAuthList = interfaceAuthMapper.selectList(Wrappers.lambdaQuery(InterfaceAuth.class)
                .in(InterfaceAuth::getRoleId, roleIdList));
        if (CollectionUtils.isNotEmpty(interfaceAuthList)) {
            List<String> interfaceIdList = interfaceAuthList.stream().map(InterfaceAuth::getInterfaceId).collect(Collectors.toList());
            tokenSession.set(GlobalConstant.LOGIN_USER_INTERFACE_AUTH_CODE_KEY, interfaceIdList);
        }

        List<String> buttonCodeList = buttonList.stream().map(MenuButton::getCode).collect(Collectors.toList());

        tokenSession.set(GlobalConstant.LOGIN_USER_AUTH_CODE_KEY, buttonCodeList);
        tokenSession.set(GlobalConstant.LOGIN_USER_ROLE_CODE_KEY, roleCodeList);
        tokenSession.set(GlobalConstant.LOGIN_USER_ROLE_ID_KEY, roleIdList);


        return result;

    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean setRoleAuth(SetRoleAuthDto dto) {

        //添加菜单权限
        Long id = dto.getId();
        List<Authorize> savedAuthList = new ArrayList<>();
        //菜单
        buildAuthorize(id, dto.getMenuIds(), AuthorizeType.MENU.getCode(), savedAuthList);
        //按钮
        buildAuthorize(id, dto.getButtonIds(), AuthorizeType.BUTTON.getCode(), savedAuthList);
        //列表字段
        buildAuthorize(id, dto.getColumnIds(), AuthorizeType.COLUMN.getCode(), savedAuthList);
        //表单字段
        buildAuthorize(id, dto.getFormIds(), AuthorizeType.FORM.getCode(), savedAuthList);

        //删除当前角色所有权限
        this.remove(Wrappers.<Authorize>query().lambda().eq(Authorize::getRoleId, id).in(Authorize::getAuthorizeType, 0, 1, 2, 3));
        //先保存角色权限
        this.saveBatch(savedAuthList);
        //再更新角色权限的缓存
        logoutByRoleId(id);
        return true;
    }

    @Override
    public RoleAuthVo getRoleAuth(Long id) {

        //如果是超级管理员 默认返回所有权限
        if (Objects.equals(id, GlobalConstant.SUPER_ADMIN_ROLE_ID)) {
            List<Menu> menus = menuMapper.selectList(Wrappers.lambdaQuery(Menu.class).select(Menu::getId));
            List<MenuButton> buttons = menuButtonMapper.selectList(Wrappers.lambdaQuery(MenuButton.class).select(MenuButton::getId));

            RoleAuthVo adminVo = new RoleAuthVo();
            adminVo.setMenuIds(menus.stream().map(Menu::getId).collect(Collectors.toList()));
            adminVo.setButtonIds(buttons.stream().map(MenuButton::getId).collect(Collectors.toList()));
            return adminVo;
        }

        //获取角色所有权限
        List<Authorize> authorizeList = authorizeMapper.selectList(Wrappers.lambdaQuery(Authorize.class).eq(Authorize::getRoleId, id));
        //将角色权限分为 菜单、按钮、列表字段和表单字段权限
        List<Long> menuIdList = new ArrayList<>();
        List<Long> btnIdList = new ArrayList<>();
        List<Long> colIdList = new ArrayList<>();
        List<Long> formIdList = new ArrayList<>();
        if (CollectionUtils.isNotEmpty(authorizeList)) {
            for (Authorize authorize : authorizeList) {
                Integer authorizeType = authorize.getAuthorizeType();
                Long authorizeId = authorize.getObjectId();
                if (authorizeType == AuthorizeType.MENU.getCode()) {
                    menuIdList.add(authorizeId);
                } else if (authorizeType == AuthorizeType.BUTTON.getCode()) {
                    btnIdList.add(authorizeId);
                } else if (authorizeType == AuthorizeType.COLUMN.getCode()) {
                    colIdList.add(authorizeId);
                } else if (authorizeType == AuthorizeType.FORM.getCode()) {
                    formIdList.add(authorizeId);
                }
            }
        }
        RoleAuthVo roleAuthVo = new RoleAuthVo();
        if (CollectionUtils.isNotEmpty(menuIdList)) {
            // 过滤掉父级菜单id
            List<Menu> menuList = menuMapper.selectList(Wrappers.<Menu>query().lambda()
                    .select(Menu::getParentId, Menu::getId)
                    .in(Menu::getId, menuIdList)
                    .eq(Menu::getEnabledMark, EnabledMark.ENABLED.getCode()));
            List<MenuTreeVo> menuTreeVos = BeanUtil.copyToList(menuList, MenuTreeVo.class);
            List<MenuTreeVo> menuTreeList = TreeUtil.build(menuTreeVos);
            buildLastChildIds(menuTreeList, roleAuthVo.getMenuIds());
        }
        if (CollectionUtils.isNotEmpty(btnIdList)) {
            // 过滤按钮id
            List<MenuButton> menuButtonList = menuButtonMapper.selectList(Wrappers.<MenuButton>query().lambda()
                    .select(MenuButton::getId).in(MenuButton::getId, btnIdList));
            roleAuthVo.setButtonIds(menuButtonList.stream().map(MenuButton::getId).collect(Collectors.toList()));
        }
        if (CollectionUtils.isNotEmpty(colIdList)) {
            // 过滤列表字段id
            List<MenuColumn> menuColumnList = menuColumnMapper.selectList(Wrappers.<MenuColumn>query().lambda()
                    .select(MenuColumn::getId).in(MenuColumn::getId, colIdList));
            roleAuthVo.setColumnIds(menuColumnList.stream().map(MenuColumn::getId).collect(Collectors.toList()));
        }
        if (CollectionUtils.isNotEmpty(formIdList)) {
            // 过滤掉按钮id
            List<MenuForm> menuList = menuFormMapper.selectList(Wrappers.<MenuForm>query().lambda()
                    .select(MenuForm::getParentId, MenuForm::getId)
                    .in(MenuForm::getId, formIdList));
            List<MenuFormTreeVo> menuFormTreeVos = BeanUtil.copyToList(menuList, MenuFormTreeVo.class);
            List<MenuFormTreeVo> menuFormTreeList = TreeUtil.build(menuFormTreeVos);
            buildLastChildIds(menuFormTreeList, roleAuthVo.getFormIds());
        }
        return roleAuthVo;
    }

    private <T extends ITreeNode<T, Long>> void buildLastChildIds(List<T> treeList, List<Long> idList) {
        if (CollectionUtils.isEmpty(treeList)) {
            return;
        }
        for (ITreeNode<T, Long> treeVo : treeList) {
            List<T> children = treeVo.getChildren();
            if (CollectionUtils.isNotEmpty(children)) {
                buildLastChildIds(children, idList);
            } else {
                idList.add(treeVo.getId());
            }
        }
    }

    /**
     * 根据roleId 登出所有用户  用于：角色权限变化，所有拥有此权限的用户 必须全部重新登录
     *
     * @param id
     */
    private void logoutByRoleId(Long id) {
        List<UserRoleRelation> relations = userRoleRelationMapper.selectList(Wrappers.lambdaQuery(UserRoleRelation.class)
                .eq(UserRoleRelation::getRoleId, id).select(UserRoleRelation::getUserId));

        List<Long> currentRoleUser = relations.stream().map(UserRoleRelation::getUserId).collect(Collectors.toList());
        currentRoleUser.forEach(StpUtil::logout);
    }

    /**
     * 构建授权数据
     *
     * @param roleId          角色id
     * @param itemIdList      菜单id
     * @param itemType        菜单类型，0-菜单，1-按钮，2-列表字段，3-表单字段
     * @param authorizeList   构建的保存对象集合
     */
    private void buildAuthorize(Long roleId, List<Long> itemIdList, Integer itemType, List<Authorize> authorizeList) {
        if (CollectionUtils.isNotEmpty(itemIdList)) {
            for (Long itemId : itemIdList) {
                Authorize authorize = new Authorize();
                authorize.setAuthorizeType(itemType);
                authorize.setObjectId(itemId);
                authorize.setRoleId(roleId);
                authorizeList.add(authorize);
            }
        }
    }
}
